﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using pub.CmdUI.Cmd;
using pub.CmdUI.UI.Control;
using pub.Interop;
using todo.Lib.NET20Fix;

namespace pub.CmdUI
{
    public class CmdUIManager : IMessageFilter
    {
        private ISet<string> cmdNames = new HashSet<string>();
        private List<ICmd> cmds;
        private List<CmdUIBinding> cmdUIs;
        private long lastCmdUIsUpdateTick;
        private bool interruptCmdUIUpdate;

        public CmdUIManager()
        {
            Application.AddMessageFilter(this);

            cmdNames = new HashSet<string>();
            cmds = new List<ICmd>();
            cmdUIs = new List<CmdUIBinding>();

            Application.Idle += Application_Idle;
        }

        private void Application_Idle(object sender, EventArgs e)
        {
//            Console.WriteLine("@@@@@@ Application_Idle.. @" + DateTime.Now);
            // update every 10s
            // if too fast, minimize/restore will be lagging
            if (Environment.TickCount - lastCmdUIsUpdateTick > 10000)
            {
                UpdateCmdUIs();
            }
        }

        public void InvalidateCmdUI()
        {
            lastCmdUIsUpdateTick = 0;
        }

        private bool ProcessInput(Message m, Keys keyData)
        {
            foreach (var cmd in cmds)
            {
                foreach (var shortcut in cmd.Shortcuts)
                {
                    if (shortcut.Keys == keyData)
                    {
                        if (cmd.State != null && cmd.State.Enabled)
                        {
                            cmd.Execute(null);
                            InvalidateCmdUI();
                            return true;
                        }
                        break;
                    }
                }
            }
            return false;
        }

        public object UpdateCmdUIs()
        {
//            Debug.Print("######## updating cmduis @" + DateTime.Now);
            interruptCmdUIUpdate = false;
            foreach (var cmdUI in cmdUIs)
            {
                Application.DoEvents();
                if (interruptCmdUIUpdate)
                {
//                    Debug.Print("@@@@@@@@@@@@ interruptCmdUIUpdate @" + DateTime.Now);
                    return null;
                }
                cmdUI.UI.Update(cmdUI.Cmd);
            }
            lastCmdUIsUpdateTick = Environment.TickCount;
            return null;
        }

        public void Bind(ICmd cmd, Control element)
        {
            if (!(element is ButtonBase))
            {
                throw new NotImplementedException();
            }
            var buttonUi = new ButtonUI(element as ButtonBase);
            buttonUi.TriggerCallback = () => cmd.Execute(buttonUi);
            cmdUIs.Add(new CmdUIBinding {Cmd = cmd, UI = buttonUi});

            Register(cmd);
        }

        public void Bind(ICmd cmd)
        {
            Bind(cmd, DummyUI.Instance);
        }

        public void Bind(ICmd cmd, IControlUI controlUi)
        {
            controlUi.TriggerCallback = () => {
                if (cmd.State.Enabled) // double check
                {
                    cmd.Execute(controlUi);
                    InvalidateCmdUI();
                }
            };
            cmdUIs.Add(new CmdUIBinding {Cmd = cmd, UI = controlUi});

            Register(cmd);
        }


        public bool PreFilterMessage(ref Message m)
        {
//            Console.WriteLine("@@@@@@ PreFilterMessage.." + m.Msg + "@" + DateTime.Now);
            interruptCmdUIUpdate = true;
            if (m.Msg == Win32.WM_LBUTTONUP ||
                m.Msg == Win32.WM_RBUTTONUP ||
                m.Msg == Win32.WM_KEYUP ||
                m.Msg == Win32.WM_SETFOCUS ||
                m.Msg == Win32.WM_KILLFOCUS)
            {
//                Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&& " + m.Msg + " @" + DateTime.Now);
                InvalidateCmdUI();
            }
            if (m.Msg == Win32.WM_KEYDOWN || m.Msg == Win32.WM_SYSKEYDOWN)
            {
//                Console.WriteLine("@@@@@@ WM_KEYDOWN " + m.LParam);
                if (m.WParam == (IntPtr) Keys.ShiftKey ||
                    m.WParam == (IntPtr) Keys.ControlKey ||
                    m.WParam == (IntPtr) Keys.Menu)
                {
                    return false;
                }

                var modifierKeys = Keys.None;
                if (Win32.GetKeyState(16) < 0)
                    modifierKeys |= Keys.Shift;
                if (Win32.GetKeyState(17) < 0)
                    modifierKeys |= Keys.Control;
                if (Win32.GetKeyState(18) < 0)
                    modifierKeys |= Keys.Alt;


                var keyData = (Keys) m.WParam | modifierKeys;
                if (ProcessInput(m, keyData))
                {
                    return true;
                }
            }
            return false;
        }

        public void Register(ICmd cmd)
        {
            if (cmdNames.Add(cmd.Name))
            {
                cmds.Add(cmd);
            }
        }
    }
}